using Sandia.MVCFramework.Common;
using Sandia.MVCFramework.Data;
using Sandia.MVCFramework.View;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace Sandia.MVCFramework.View
{
    /// <summary>
    /// LoggingView is an IView designed to display logging
    /// messages produced by the Microsoft Enterprise Library
    /// logging system
    /// </summary>        
    public class LoggingView : AbstractStandardView
    {
        ListBox listBox;

        /// <summary>
        /// The main panel on the form.
        /// </summary>
        private TableLayoutPanel mainPanel = new TableLayoutPanel();

        /// <summary>
        /// Logging view's icon
        /// </summary>        
        public static string IMAGE = "Sandia.MVCFramework.View.DocumentHS.bmp";

        /// <summary>
        /// Logging view's name
        /// </summary>
        public static string NAME = "Event Log";

        /// <summary>
        /// The constructor for LoggingView.
        /// </summary>
        public LoggingView()
            : base(null, null)
        {
        }
        
        /// <summary>
        /// Handles published data for this view.
        /// </summary>
        /// <param name="dataPacket">
        /// The published data packet to handle.
        /// </param>
        public override void HandlePublishedData(
            DataPacket dataPacket)
        {            
            // Nothing to do (no subscriptions)
        }        

        /// <summary>
        /// A method called by AbstractStandardView on construction that allows
        /// inheriting classes to add stuff to the display.
        /// </summary>
        protected override void setupView()
        {
            this.mainPanel.Dock = DockStyle.Fill;
            this.mainPanel.AutoScroll = true;
            this.mainPanel.ColumnCount = 1;
            this.mainPanel.RowCount = 2;
            this.Controls.Add(this.mainPanel);
            
            // Create list box for log entries
            this.listBox = new ListBox();                        
            this.listBox.Name = "Log Output:";
            this.listBox.Dock = DockStyle.Fill;
            this.listBox.HorizontalScrollbar = true;
            mainPanel.Controls.Add(listBox, 0, 0);

            // Create clear button
            Button clearBtn = new Button();
            clearBtn.Text = "Clear";
            clearBtn.Name = "Clear Button";
            clearBtn.Dock = DockStyle.Right;
            clearBtn.Click += new EventHandler(clearBtn_Click);
            clearBtn.Height = 30;
            mainPanel.Controls.Add(clearBtn, 0, 1);
            
            // Setup column widths and row heights
            this.mainPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
            this.mainPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
            this.mainPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, clearBtn.Height));
            
            // View icon
            Assembly thisExe = Assembly.GetExecutingAssembly();
            Stream file =
                thisExe.GetManifestResourceStream(LoggingView.IMAGE);
            Bitmap image = (Bitmap)Bitmap.FromStream(file);
            image.MakeTransparent();
            this.image = image;
            
            // View name
            this.name = LoggingView.NAME;
        }

        /// <summary>
        /// The list of IData types that this subscriber would like to subscribe
        /// to.
        /// </summary>
        public override IList<Type> DesiredSubscriptions
        {
            get
            {
                // No subscriptions.
                return new List<Type>();
            }
        }

        /// <summary>
        /// Called when clear button is clicked.
        /// </summary>
        /// <param name="sender">The button that was clicked.</param>
        /// <param name="e">The event arguments.</param>
        void clearBtn_Click(
            object sender,
            EventArgs e)
        {
            if (sender is Button)
            {
                this.listBox.Items.Clear();
            }
        }

        /// <summary>
        /// This delegate enables asynchronous calls for
        /// adding a log entry from a non-UI thread
        /// </summary>
        /// <param name="entry">Log entry to
        /// add to the logging view</param>
        private delegate void AddLogEntryCallback(string entry);

        /// <summary>
        /// Add the specified log entry to the
        /// logging view
        /// </summary>
        /// <remarks>New entries are added
        /// at the top of the view</remarks>
        /// <param name="entry">Log entry to
        /// add to the logging view</param>
        public void AddLogEntry(string entry)
        {
            // Check to see if Invoke is required
            if (this.listBox.InvokeRequired)
            {
                // Perform invoke
                AddLogEntryCallback theDelegate = new AddLogEntryCallback(AddLogEntry);                
                
                // BAD CODE!!!
                // Can lead to a deadlock for some reason - disabling for now [20070424 ZOB]
                // The deadlock occurs in Microsoft.Practices.EnterpriseLibrary.Logging.LogSource.cs,
                // at line 127 - the monitor is entered and then just hangs there; meanwhile
                // another thread is suspended within LoggingViewTraceListener trying
                // to call this (AddLogEntry) method... probably the reason is that the 
                // invoke goes back to the main thread, but its blocked waiting for
                // this log entry to finish, resulting in a deadlock.
                //this.Invoke(theDelegate, new object[] { entry });

                // Solution - perform the Invoke asynchronously rather
                // that waiting for it to return, thus avoiding deadlock
                this.BeginInvoke(theDelegate, new object[] { entry });
            }
            else
            {
                if (this.listBox != null)
                {
                    this.listBox.BeginUpdate();
                    this.listBox.Items.Insert(0, " ");

                    string[] lines = entry.Split(new char[1] { '\n' });
                    for (int i = lines.Length; i > 0; i--)
                    {
                        this.listBox.Items.Insert(0, lines[i - 1].Trim());
                    }
                    this.listBox.EndUpdate();
                }
            }
        }

        /// <summary>
        /// Called when MVC Framework initialization has completed and
        /// transactions can begin.
        /// </summary>
        /// <remarks>
        /// IRequests and IData should not be sent before this method is called, 
        /// as this can cause race conditions while an application is 
        /// initializing.
        /// </remarks>
        public override void Start()
        {
            // Nothing to do here.
        }
    }
}

